package ca.unbc.cpsc672.rcmd;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;

import ca.unbc.cpsc672.fuzzy.FuzzySet;
import ca.unbc.cpsc672.fuzzy.IOUtils;
import ca.unbc.cpsc672.fuzzy.RuleSet;
import ca.unbc.cpsc672.fuzzy.function.MembershipFunction;

public class RcommandationGenerator {
	private Map<String, FuzzySet> setMap;
	private Map<String, RuleSet> rulesMap;
	private BusinessInfo[] infoAry;
	private RcmdOptions options;

	public RcommandationGenerator(RcmdOptions options) throws SQLException,
			IOException {
		infoAry = DBUtils.getInfo();
		setMap = IOUtils.loadFuzzySet();
		rulesMap = IOUtils.loadRuls(setMap);
		this.options = options;
	}

	public BusinessInfo[] run() {
		// distance rule, piecewise
		distanceRule();
		// time rule, discrete
		timeRule();
		// service & taste, fuzzy inference
		stRule();
		// popularity rule, only use one fuzzy set
		popRule();
		// generate recommendations
		rcmd();
		return infoAry;
	}

	private void distanceRule() {
		FuzzySet set = setMap.get("distance");
		for (MembershipFunction mf : set.getAllMembershipFunction()) {
			mf.setConsentrate(options.cDistance);
		}
		RuleSet rset = rulesMap.get("distance_rule");
		for (BusinessInfo info : infoAry) {
			// distance
			double dis = Math.sqrt(Math
					.pow(info.latitude - options.latitude, 2)
					+ Math.pow(info.longitude - options.longititude, 2));
			// get support level for each input
			info.distance_rule = rset.infer(dis * 400);
			if (Double.isNaN(info.distance_rule) || info.distance_rule == 0)
				info.distance_rule = 1;
		}
	}

	private void timeRule() {
		FuzzySet set = setMap.get("lasttime");
		for (MembershipFunction mf : set.getAllMembershipFunction()) {
			mf.setConsentrate(options.cTime);
		}
		RuleSet rset = rulesMap.get("time_rule");
		for (BusinessInfo info : infoAry) {
			// discrete, time = 0 then rate = 0
			if (options.time >= info.mo_close || options.time <= info.mo_open) {
				info.time_rule = 0;
				continue;
			}
			info.time_rule = rset.infer(info.mo_close - options.time);
			if (Double.isNaN(info.time_rule) || info.time_rule == 0)
				info.time_rule = 1;
		}
	}

	private void stRule() {
		FuzzySet set = setMap.get("taste");
		for (MembershipFunction mf : set.getAllMembershipFunction()) {
			mf.setConsentrate(options.cTaste);
		}
		set = setMap.get("service");
		for (MembershipFunction mf : set.getAllMembershipFunction()) {
			mf.setConsentrate(options.cService);
		}
		RuleSet rset = rulesMap.get("taste&service");
		for (BusinessInfo info : infoAry) {
			// if..and...then..., fuzzy inference
			info.st_rule = rset.infer(info.taste, info.service);
			if (Double.isNaN(info.st_rule) || info.st_rule == 0)
				info.st_rule = 1;
		}
	}

	private void popRule() {
		FuzzySet set = setMap.get("popularity");
		for (MembershipFunction mf : set.getAllMembershipFunction()) {
			mf.setConsentrate(options.cPopularity);
		}
		MembershipFunction mf = options.mf;
		if (mf == null)
			return;
		for (BusinessInfo info : infoAry) {
			// directly by membership function
			info.pop_rule = mf.calcProperbility(info.popularity);
			if (Double.isNaN(info.pop_rule))
				info.pop_rule = 1;
		}
	}

	private void rcmd() {
		// final rate: multiple all support levels
		for (BusinessInfo info : infoAry) {
			info.rate = info.pop_rule * info.st_rule * info.distance_rule
					* info.time_rule * info.stars;
		}
		// sort by their rate, we will recommend restaurant with the highest
		// rate
		Arrays.sort(infoAry, new Comparator<BusinessInfo>() {

			@Override
			public int compare(BusinessInfo o1, BusinessInfo o2) {
				return Double.compare(o2.rate, o1.rate);
			}
		});
		for (BusinessInfo info : infoAry) {
			System.out.println("rate:" + info.rate + ", pop:" + info.pop_rule
					+ ", service&taste" + info.st_rule + ", distance:"
					+ info.distance_rule + ", time:" + info.time_rule
					+ ", stars:" + info.stars);
		}
	}
}
